From 0992ef407ddab5cb67830ab9bb5475a7ef8d4149 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20H=C3=A4rdeman?= Date: Wed, 15 Oct 2025 18:21:58 +0200 Subject: [PATCH] luci-mod-network: organize "dnsrecords" tab in dhcp/dns views MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Delete the tab from dhcp.js, sort the options that belong to it in dns.js. Signed-off-by: David Härdeman --- .../resources/view/network/dhcp.js | 241 +-------- .../luci-static/resources/view/network/dns.js | 494 +++++++++--------- 2 files changed, 249 insertions(+), 486 deletions(-) diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js index f029a3a7b8..efd94d2586 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js @@ -8,7 +8,6 @@ 'require network'; 'require validation'; 'require tools.widgets as widgets'; -'require tools.dnsrecordhandlers as drh'; var callHostHints, callDUIDHints, callDHCPLeases, CBILeaseStatus, CBILease6Status; var callUfpList; @@ -294,7 +293,7 @@ return view.extend({ render: function([hosts, duids, pools, networks, macdata]) { var has_dhcpv6 = L.hasSystemFeature('dnsmasq', 'dhcpv6') || L.hasSystemFeature('odhcpd'), - m, s, o, ss, so, dnss; + m, s, o, ss, so; let noi18nstrings = { etc_hosts: '/etc/hosts', @@ -392,7 +391,6 @@ return view.extend({ s.tab('general', _('General')); s.tab('devices', _('Devices & Ports')); - s.tab('dnsrecords', _('DNS Records')); s.tab('dnssecopt', _('DNSSEC')); s.tab('filteropts', _('Filter')); s.tab('forward', _('Forwards')); @@ -903,123 +901,6 @@ return view.extend({ so.value('39', _('40: LoongArch 64-bit UEFI boot from HTTP')); so.value('41', _('41: ARM rpiboot')); - o = s.taboption('dnsrecords', form.SectionValue, '__dnsrecords__', form.TypedSection, '__dnsrecords__'); - - dnss = o.subsection; - - dnss.anonymous = true; - dnss.cfgsections = function() { return [ '__dnsrecords__' ] }; - - dnss.tab('hosts', _('Hostnames')); - dnss.tab('srvhosts', _('SRV')); - dnss.tab('mxhosts', _('MX')); - dnss.tab('cnamehosts', _('CNAME')); - dnss.tab('dnsrr', _('DNS-RR')); - - o = dnss.taboption('srvhosts', form.SectionValue, '__srvhosts__', form.TableSection, 'srvhost', null, - _('Bind service records to a domain name: specify the location of services. See RFC2782.').format('https://datatracker.ietf.org/doc/html/rfc2782') - + '
' + _('_service: _sip, _ldap, _imap, _stun, _xmpp-client, … . (Note: while _http is possible, no browsers support SRV records.)') - + '
' + _('_proto: _tcp, _udp, _sctp, _quic, … .') - + '
' + _('You may add multiple records for the same Target.') - + '
' + _('Larger weights (of the same prio) are given a proportionately higher probability of being selected.')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - ss.rowcolors = true; - - so = ss.option(form.Value, 'srv', _('SRV'), _('Syntax:') + ' ' + '_service._proto.example.com.'); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = '_sip._tcp.example.com.'; - - so = ss.option(form.Value, 'target', _('Target'), _('CNAME or fqdn')); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = 'sip.example.com.'; - - so = ss.option(form.Value, 'port', _('Port')); - so.rmempty = false; - so.datatype = 'port'; - so.placeholder = '5060'; - - so = ss.option(form.Value, 'class', _('Priority'), _('Ordinal: lower comes first.')); - so.rmempty = true; - so.datatype = 'range(0,65535)'; - so.placeholder = '10'; - - so = ss.option(form.Value, 'weight', _('Weight')); - so.rmempty = true; - so.datatype = 'range(0,65535)'; - so.placeholder = '50'; - - o = dnss.taboption('mxhosts', form.SectionValue, '__mxhosts__', form.TableSection, 'mxhost', null, - _('Bind service records to a domain name: specify the location of services.') - + '
' + _('You may add multiple records for the same domain.')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - ss.rowcolors = true; - ss.nodescriptions = true; - - so = ss.option(form.Value, 'domain', _('Domain')); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = 'example.com.'; - - so = ss.option(form.Value, 'relay', _('Relay')); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = 'relay.example.com.'; - - so = ss.option(form.Value, 'pref', _('Priority'), _('Ordinal: lower comes first.')); - so.rmempty = true; - so.datatype = 'range(0,65535)'; - so.placeholder = '0'; - - o = dnss.taboption('cnamehosts', form.SectionValue, '__cname__', form.TableSection, 'cname', null, - _('Set an alias for a hostname.')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - ss.rowcolors = true; - ss.nodescriptions = true; - - so = ss.option(form.Value, 'cname', _('Domain')); - so.rmempty = false; - so.validate = validateHostname; - so.placeholder = 'www.example.com.'; - - so = ss.option(form.Value, 'target', _('Target')); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = 'example.com.'; - - o = dnss.taboption('hosts', form.SectionValue, '__hosts__', form.GridSection, 'domain', null, - _('Hostnames are used to bind a domain name to an IP address. This setting is redundant for hostnames already configured with static leases, but it can be useful to rebind an FQDN.')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - - so = ss.option(form.Value, 'name', _('Hostname')); - so.rmempty = false; - so.datatype = 'hostname'; - - so = ss.option(form.Value, 'ip', _('IP address')); - so.rmempty = false; - so.datatype = 'ipaddr("nomask")'; - var ipaddrs = {}; Object.keys(hosts).forEach(function(mac) { @@ -1029,126 +910,6 @@ return view.extend({ ipaddrs[addrs[i]] = hosts[mac].name || mac; }); - L.sortedKeys(ipaddrs, null, 'addr').forEach(function(ipv4) { - so.value(ipv4, '%s (%s)'.format(ipv4, ipaddrs[ipv4])); - }); - - o = dnss.taboption('dnsrr', form.SectionValue, '__dnsrr__', form.GridSection, 'dnsrr', null, - _('Set an arbitrary resource record (RR) type.') + '
' + - _('Hexdata is automatically en/decoded on save and load')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - ss.rowcolors = true; - ss.nodescriptions = true; - - function hexdecodeload(section_id) { - let value = uci.get('dhcp', section_id, 'hexdata') || ''; - // Remove any spaces or colons from the hex string - they're allowed - value = value.replace(/[\s:]/g, ''); - // Hex-decode the string before displaying - let decodedString = ''; - for (let i = 0; i < value.length; i += 2) { - decodedString += String.fromCharCode(parseInt(value.substr(i, 2), 16)); - } - return decodedString; - } - - function hexencodesave(section, value) { - if (!value || value.length === 0) { - uci.unset('dhcp', section, 'hexdata'); - return; - } - // Hex-encode the string before saving - const encodedArr = value.split('').map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(''); - uci.set('dhcp', section, this.option, encodedArr); - } - - so = ss.option(form.Value, 'rrname', _('Resource Record Name')); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = 'svcb.example.com.'; - - so = ss.option(form.Value, 'rrnumber', _('Resource Record Number')); - so.rmempty = false; - so.datatype = 'uinteger'; - so.placeholder = '64'; - - so = ss.option(form.Value, '_hexdata', _('Raw Data')); - so.rmempty = true; - so.datatype = 'string'; - so.placeholder = 'free-form string'; - so.load = hexdecodeload; - so.write = hexencodesave; - so.modalonly = true; - so.depends({ rrnumber: '65', '!reverse': true }); - - so = ss.option(form.DummyValue, 'hexdata', _('Hex Data')); - so.width = '50%'; - so.rawhtml = true; - so.load = function(section_id) { - let hexdata = uci.get('dhcp', section_id, 'hexdata') || ''; - hexdata = hexdata.replace(/[:]/g, ''); - return hexdata.replace(/(.{2})/g, '$1 '); - }; - - function writetype65(section_id, value) { - let rrnum = uci.get('dhcp', section_id, 'rrnumber'); - if (rrnum !== '65') return; - - let priority = parseInt(this.section.formvalue(section_id, '_svc_priority'), 10); - let target = this.section.formvalue(section_id, '_svc_target') || '.'; - let params = value.trim().split('\n').map(l => l.trim()).filter(Boolean); - - const hex = drh.buildSvcbHex(priority, target, params); - uci.set('dhcp', section_id, 'hexdata', hex); - }; - - function loadtype65(section_id) { - let rrnum = uci.get('dhcp', section_id, 'rrnumber'); - if (rrnum !== '65') return null; - - let hexdata = uci.get('dhcp', section_id, 'hexdata'); - return drh.parseSvcbHex(hexdata); - }; - - // Type 65 builder fields (hidden unless rrnumber === 65) - so = ss.option(form.Value, '_svc_priority', _('Svc Priority')); - so.placeholder = 1; - so.datatype = 'and(uinteger,min(0),max(65535))' - so.modalonly = true; - so.depends({ rrnumber: '65' }); - so.write = writetype65; - so.load = function(section_id) { - const parsed = loadtype65(section_id); - return parsed?.priority?.toString() || ''; - }; - - so = ss.option(form.Value, '_svc_target', _('Svc Target')); - so.placeholder = 'svc.example.com.'; - so.dataype = 'hostname'; - so.modalonly = true; - so.depends({ rrnumber: '65' }); - so.write = writetype65; - so.load = function(section_id) { - const parsed = loadtype65(section_id); - return parsed?.target || ''; - }; - - so = ss.option(form.TextValue, '_svc_params', _('Svc Parameters')); - so.placeholder = 'alpn=h2,h3\nipv4hint=192.0.2.1,192.0.2.2\nipv6hint=2001:db8::1,2001:db8::2\nport=8000'; - so.modalonly = true; - so.rows = 4; - so.depends({ rrnumber: '65' }); - so.write = writetype65; - so.load = function(section_id) { - const parsed = loadtype65(section_id); - return parsed?.params?.join('\n') || ''; - }; - o = s.taboption('ipsets', form.SectionValue, '__ipsets__', form.GridSection, 'ipset', null, _('List of IP sets to populate with the IPs of DNS lookup results of the FQDNs also specified here.') + '
' + _('The netfilter components below are only regarded when running fw4.')); diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dns.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dns.js index 9818181e86..50dbd220ab 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dns.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dns.js @@ -521,6 +521,254 @@ return view.extend({ o.depends('queryport', ''); // End devices + // Begin dnsrecords + o = s.taboption('dnsrecords', form.SectionValue, '__dnsrecords__', form.TypedSection, '__dnsrecords__'); + + dnss = o.subsection; + + dnss.anonymous = true; + dnss.cfgsections = function() { return [ '__dnsrecords__' ] }; + + dnss.tab('hosts', _('Hostnames')); + dnss.tab('srvhosts', _('SRV')); + dnss.tab('mxhosts', _('MX')); + dnss.tab('cnamehosts', _('CNAME')); + dnss.tab('dnsrr', _('DNS-RR')); + + o = dnss.taboption('srvhosts', form.SectionValue, '__srvhosts__', form.TableSection, 'srvhost', null, + _('Bind service records to a domain name: specify the location of services. See RFC2782.').format('https://datatracker.ietf.org/doc/html/rfc2782') + + '
' + _('_service: _sip, _ldap, _imap, _stun, _xmpp-client, … . (Note: while _http is possible, no browsers support SRV records.)') + + '
' + _('_proto: _tcp, _udp, _sctp, _quic, … .') + + '
' + _('You may add multiple records for the same Target.') + + '
' + _('Larger weights (of the same prio) are given a proportionately higher probability of being selected.')); + + ss = o.subsection; + + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.rowcolors = true; + + so = ss.option(form.Value, 'srv', _('SRV'), _('Syntax:') + ' ' + '_service._proto.example.com.'); + so.rmempty = false; + so.datatype = 'hostname'; + so.placeholder = '_sip._tcp.example.com.'; + + so = ss.option(form.Value, 'target', _('Target'), _('CNAME or fqdn')); + so.rmempty = false; + so.datatype = 'hostname'; + so.placeholder = 'sip.example.com.'; + + so = ss.option(form.Value, 'port', _('Port')); + so.rmempty = false; + so.datatype = 'port'; + so.placeholder = '5060'; + + so = ss.option(form.Value, 'class', _('Priority'), _('Ordinal: lower comes first.')); + so.rmempty = true; + so.datatype = 'range(0,65535)'; + so.placeholder = '10'; + + so = ss.option(form.Value, 'weight', _('Weight')); + so.rmempty = true; + so.datatype = 'range(0,65535)'; + so.placeholder = '50'; + + o = dnss.taboption('mxhosts', form.SectionValue, '__mxhosts__', form.TableSection, 'mxhost', null, + _('Bind service records to a domain name: specify the location of services.') + + '
' + _('You may add multiple records for the same domain.')); + + ss = o.subsection; + + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.rowcolors = true; + ss.nodescriptions = true; + + so = ss.option(form.Value, 'domain', _('Domain')); + so.rmempty = false; + so.datatype = 'hostname'; + so.placeholder = 'example.com.'; + + so = ss.option(form.Value, 'relay', _('Relay')); + so.rmempty = false; + so.datatype = 'hostname'; + so.placeholder = 'relay.example.com.'; + + so = ss.option(form.Value, 'pref', _('Priority'), _('Ordinal: lower comes first.')); + so.rmempty = true; + so.datatype = 'range(0,65535)'; + so.placeholder = '0'; + + o = dnss.taboption('cnamehosts', form.SectionValue, '__cname__', form.TableSection, 'cname', null, + _('Set an alias for a hostname.')); + + ss = o.subsection; + + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.rowcolors = true; + ss.nodescriptions = true; + + so = ss.option(form.Value, 'cname', _('Domain')); + so.rmempty = false; + so.validate = validateHostname; + so.placeholder = 'www.example.com.'; + + so = ss.option(form.Value, 'target', _('Target')); + so.rmempty = false; + so.datatype = 'hostname'; + so.placeholder = 'example.com.'; + + o = dnss.taboption('hosts', form.SectionValue, '__hosts__', form.GridSection, 'domain', null, + _('Hostnames are used to bind a domain name to an IP address. This setting is redundant for hostnames already configured with static leases, but it can be useful to rebind an FQDN.')); + + ss = o.subsection; + + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + + so = ss.option(form.Value, 'name', _('Hostname')); + so.rmempty = false; + so.datatype = 'hostname'; + + so = ss.option(form.Value, 'ip', _('IP address')); + so.rmempty = false; + so.datatype = 'ipaddr("nomask")'; + + var ipaddrs = {}; + + Object.keys(hosts).forEach(function(mac) { + var addrs = L.toArray(hosts[mac].ipaddrs || hosts[mac].ipv4); + + for (var i = 0; i < addrs.length; i++) + ipaddrs[addrs[i]] = hosts[mac].name || mac; + }); + + L.sortedKeys(ipaddrs, null, 'addr').forEach(function(ipv4) { + so.value(ipv4, '%s (%s)'.format(ipv4, ipaddrs[ipv4])); + }); + + o = dnss.taboption('dnsrr', form.SectionValue, '__dnsrr__', form.GridSection, 'dnsrr', null, + _('Set an arbitrary resource record (RR) type.') + '
' + + _('Hexdata is automatically en/decoded on save and load')); + + ss = o.subsection; + + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.rowcolors = true; + ss.nodescriptions = true; + + function hexdecodeload(section_id) { + let value = uci.get('dhcp', section_id, 'hexdata') || ''; + // Remove any spaces or colons from the hex string - they're allowed + value = value.replace(/[\s:]/g, ''); + // Hex-decode the string before displaying + let decodedString = ''; + for (let i = 0; i < value.length; i += 2) { + decodedString += String.fromCharCode(parseInt(value.substr(i, 2), 16)); + } + return decodedString; + } + + function hexencodesave(section, value) { + if (!value || value.length === 0) { + uci.unset('dhcp', section, 'hexdata'); + return; + } + // Hex-encode the string before saving + const encodedArr = value.split('').map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(''); + uci.set('dhcp', section, this.option, encodedArr); + } + + so = ss.option(form.Value, 'rrname', _('Resource Record Name')); + so.rmempty = false; + so.datatype = 'hostname'; + so.placeholder = 'svcb.example.com.'; + + so = ss.option(form.Value, 'rrnumber', _('Resource Record Number')); + so.rmempty = false; + so.datatype = 'uinteger'; + so.placeholder = '64'; + + so = ss.option(form.Value, '_hexdata', _('Raw Data')); + so.rmempty = true; + so.datatype = 'string'; + so.placeholder = 'free-form string'; + so.load = hexdecodeload; + so.write = hexencodesave; + so.modalonly = true; + so.depends({ rrnumber: '65', '!reverse': true }); + + so = ss.option(form.DummyValue, 'hexdata', _('Hex Data')); + so.width = '50%'; + so.rawhtml = true; + so.load = function(section_id) { + let hexdata = uci.get('dhcp', section_id, 'hexdata') || ''; + hexdata = hexdata.replace(/[:]/g, ''); + return hexdata.replace(/(.{2})/g, '$1 '); + }; + + function writetype65(section_id, value) { + let rrnum = uci.get('dhcp', section_id, 'rrnumber'); + if (rrnum !== '65') return; + + let priority = parseInt(this.section.formvalue(section_id, '_svc_priority'), 10); + let target = this.section.formvalue(section_id, '_svc_target') || '.'; + let params = value.trim().split('\n').map(l => l.trim()).filter(Boolean); + + const hex = drh.buildSvcbHex(priority, target, params); + uci.set('dhcp', section_id, 'hexdata', hex); + }; + + function loadtype65(section_id) { + let rrnum = uci.get('dhcp', section_id, 'rrnumber'); + if (rrnum !== '65') return null; + + let hexdata = uci.get('dhcp', section_id, 'hexdata'); + return drh.parseSvcbHex(hexdata); + }; + + // Type 65 builder fields (hidden unless rrnumber === 65) + so = ss.option(form.Value, '_svc_priority', _('Svc Priority')); + so.placeholder = 1; + so.datatype = 'and(uinteger,min(0),max(65535))' + so.modalonly = true; + so.depends({ rrnumber: '65' }); + so.write = writetype65; + so.load = function(section_id) { + const parsed = loadtype65(section_id); + return parsed?.priority?.toString() || ''; + }; + + so = ss.option(form.Value, '_svc_target', _('Svc Target')); + so.placeholder = 'svc.example.com.'; + so.dataype = 'hostname'; + so.modalonly = true; + so.depends({ rrnumber: '65' }); + so.write = writetype65; + so.load = function(section_id) { + const parsed = loadtype65(section_id); + return parsed?.target || ''; + }; + + so = ss.option(form.TextValue, '_svc_params', _('Svc Parameters')); + so.placeholder = 'alpn=h2,h3\nipv4hint=192.0.2.1,192.0.2.2\nipv6hint=2001:db8::1,2001:db8::2\nport=8000'; + so.modalonly = true; + so.rows = 4; + so.depends({ rrnumber: '65' }); + so.write = writetype65; + so.load = function(section_id) { + const parsed = loadtype65(section_id); + return parsed?.params?.join('\n') || ''; + }; + // End dnsrecords + s.taboption('filteropts', form.Flag, 'domainneeded', _('Domain required'), _('Never forward DNS queries which lack dots or domain parts.') + '
' + @@ -982,252 +1230,6 @@ return view.extend({ so.value('39', _('40: LoongArch 64-bit UEFI boot from HTTP')); so.value('41', _('41: ARM rpiboot')); - o = s.taboption('dnsrecords', form.SectionValue, '__dnsrecords__', form.TypedSection, '__dnsrecords__'); - - dnss = o.subsection; - - dnss.anonymous = true; - dnss.cfgsections = function() { return [ '__dnsrecords__' ] }; - - dnss.tab('hosts', _('Hostnames')); - dnss.tab('srvhosts', _('SRV')); - dnss.tab('mxhosts', _('MX')); - dnss.tab('cnamehosts', _('CNAME')); - dnss.tab('dnsrr', _('DNS-RR')); - - o = dnss.taboption('srvhosts', form.SectionValue, '__srvhosts__', form.TableSection, 'srvhost', null, - _('Bind service records to a domain name: specify the location of services. See RFC2782.').format('https://datatracker.ietf.org/doc/html/rfc2782') - + '
' + _('_service: _sip, _ldap, _imap, _stun, _xmpp-client, … . (Note: while _http is possible, no browsers support SRV records.)') - + '
' + _('_proto: _tcp, _udp, _sctp, _quic, … .') - + '
' + _('You may add multiple records for the same Target.') - + '
' + _('Larger weights (of the same prio) are given a proportionately higher probability of being selected.')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - ss.rowcolors = true; - - so = ss.option(form.Value, 'srv', _('SRV'), _('Syntax:') + ' ' + '_service._proto.example.com.'); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = '_sip._tcp.example.com.'; - - so = ss.option(form.Value, 'target', _('Target'), _('CNAME or fqdn')); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = 'sip.example.com.'; - - so = ss.option(form.Value, 'port', _('Port')); - so.rmempty = false; - so.datatype = 'port'; - so.placeholder = '5060'; - - so = ss.option(form.Value, 'class', _('Priority'), _('Ordinal: lower comes first.')); - so.rmempty = true; - so.datatype = 'range(0,65535)'; - so.placeholder = '10'; - - so = ss.option(form.Value, 'weight', _('Weight')); - so.rmempty = true; - so.datatype = 'range(0,65535)'; - so.placeholder = '50'; - - o = dnss.taboption('mxhosts', form.SectionValue, '__mxhosts__', form.TableSection, 'mxhost', null, - _('Bind service records to a domain name: specify the location of services.') - + '
' + _('You may add multiple records for the same domain.')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - ss.rowcolors = true; - ss.nodescriptions = true; - - so = ss.option(form.Value, 'domain', _('Domain')); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = 'example.com.'; - - so = ss.option(form.Value, 'relay', _('Relay')); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = 'relay.example.com.'; - - so = ss.option(form.Value, 'pref', _('Priority'), _('Ordinal: lower comes first.')); - so.rmempty = true; - so.datatype = 'range(0,65535)'; - so.placeholder = '0'; - - o = dnss.taboption('cnamehosts', form.SectionValue, '__cname__', form.TableSection, 'cname', null, - _('Set an alias for a hostname.')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - ss.rowcolors = true; - ss.nodescriptions = true; - - so = ss.option(form.Value, 'cname', _('Domain')); - so.rmempty = false; - so.validate = validateHostname; - so.placeholder = 'www.example.com.'; - - so = ss.option(form.Value, 'target', _('Target')); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = 'example.com.'; - - o = dnss.taboption('hosts', form.SectionValue, '__hosts__', form.GridSection, 'domain', null, - _('Hostnames are used to bind a domain name to an IP address. This setting is redundant for hostnames already configured with static leases, but it can be useful to rebind an FQDN.')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - - so = ss.option(form.Value, 'name', _('Hostname')); - so.rmempty = false; - so.datatype = 'hostname'; - - so = ss.option(form.Value, 'ip', _('IP address')); - so.rmempty = false; - so.datatype = 'ipaddr("nomask")'; - - var ipaddrs = {}; - - Object.keys(hosts).forEach(function(mac) { - var addrs = L.toArray(hosts[mac].ipaddrs || hosts[mac].ipv4); - - for (var i = 0; i < addrs.length; i++) - ipaddrs[addrs[i]] = hosts[mac].name || mac; - }); - - L.sortedKeys(ipaddrs, null, 'addr').forEach(function(ipv4) { - so.value(ipv4, '%s (%s)'.format(ipv4, ipaddrs[ipv4])); - }); - - o = dnss.taboption('dnsrr', form.SectionValue, '__dnsrr__', form.GridSection, 'dnsrr', null, - _('Set an arbitrary resource record (RR) type.') + '
' + - _('Hexdata is automatically en/decoded on save and load')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - ss.rowcolors = true; - ss.nodescriptions = true; - - function hexdecodeload(section_id) { - let value = uci.get('dhcp', section_id, 'hexdata') || ''; - // Remove any spaces or colons from the hex string - they're allowed - value = value.replace(/[\s:]/g, ''); - // Hex-decode the string before displaying - let decodedString = ''; - for (let i = 0; i < value.length; i += 2) { - decodedString += String.fromCharCode(parseInt(value.substr(i, 2), 16)); - } - return decodedString; - } - - function hexencodesave(section, value) { - if (!value || value.length === 0) { - uci.unset('dhcp', section, 'hexdata'); - return; - } - // Hex-encode the string before saving - const encodedArr = value.split('').map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(''); - uci.set('dhcp', section, this.option, encodedArr); - } - - so = ss.option(form.Value, 'rrname', _('Resource Record Name')); - so.rmempty = false; - so.datatype = 'hostname'; - so.placeholder = 'svcb.example.com.'; - - so = ss.option(form.Value, 'rrnumber', _('Resource Record Number')); - so.rmempty = false; - so.datatype = 'uinteger'; - so.placeholder = '64'; - - so = ss.option(form.Value, '_hexdata', _('Raw Data')); - so.rmempty = true; - so.datatype = 'string'; - so.placeholder = 'free-form string'; - so.load = hexdecodeload; - so.write = hexencodesave; - so.modalonly = true; - so.depends({ rrnumber: '65', '!reverse': true }); - - so = ss.option(form.DummyValue, 'hexdata', _('Hex Data')); - so.width = '50%'; - so.rawhtml = true; - so.load = function(section_id) { - let hexdata = uci.get('dhcp', section_id, 'hexdata') || ''; - hexdata = hexdata.replace(/[:]/g, ''); - return hexdata.replace(/(.{2})/g, '$1 '); - }; - - function writetype65(section_id, value) { - let rrnum = uci.get('dhcp', section_id, 'rrnumber'); - if (rrnum !== '65') return; - - let priority = parseInt(this.section.formvalue(section_id, '_svc_priority'), 10); - let target = this.section.formvalue(section_id, '_svc_target') || '.'; - let params = value.trim().split('\n').map(l => l.trim()).filter(Boolean); - - const hex = drh.buildSvcbHex(priority, target, params); - uci.set('dhcp', section_id, 'hexdata', hex); - }; - - function loadtype65(section_id) { - let rrnum = uci.get('dhcp', section_id, 'rrnumber'); - if (rrnum !== '65') return null; - - let hexdata = uci.get('dhcp', section_id, 'hexdata'); - return drh.parseSvcbHex(hexdata); - }; - - // Type 65 builder fields (hidden unless rrnumber === 65) - so = ss.option(form.Value, '_svc_priority', _('Svc Priority')); - so.placeholder = 1; - so.datatype = 'and(uinteger,min(0),max(65535))' - so.modalonly = true; - so.depends({ rrnumber: '65' }); - so.write = writetype65; - so.load = function(section_id) { - const parsed = loadtype65(section_id); - return parsed?.priority?.toString() || ''; - }; - - so = ss.option(form.Value, '_svc_target', _('Svc Target')); - so.placeholder = 'svc.example.com.'; - so.dataype = 'hostname'; - so.modalonly = true; - so.depends({ rrnumber: '65' }); - so.write = writetype65; - so.load = function(section_id) { - const parsed = loadtype65(section_id); - return parsed?.target || ''; - }; - - so = ss.option(form.TextValue, '_svc_params', _('Svc Parameters')); - so.placeholder = 'alpn=h2,h3\nipv4hint=192.0.2.1,192.0.2.2\nipv6hint=2001:db8::1,2001:db8::2\nport=8000'; - so.modalonly = true; - so.rows = 4; - so.depends({ rrnumber: '65' }); - so.write = writetype65; - so.load = function(section_id) { - const parsed = loadtype65(section_id); - return parsed?.params?.join('\n') || ''; - }; - o = s.taboption('ipsets', form.SectionValue, '__ipsets__', form.GridSection, 'ipset', null, _('List of IP sets to populate with the IPs of DNS lookup results of the FQDNs also specified here.') + '
' + _('The netfilter components below are only regarded when running fw4.')); -- 2.30.2